package main

import (
	"archive/zip"
	"crypto/aes"
	"crypto/sha1"
	"database/sql"
	"embed"
	"encoding/hex"
	"encoding/json"
	"errors"
	"export/server"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/pquerna/otp/totp"
	"gopkg.in/yaml.v2"
)

// Config 结构体用于解析配置文件
type Config struct {
	Server     ServerConfig `yaml:"server"`
	Log        LogConfig    `yaml:"log"`
	Databases  []Database   `yaml:"databases"`
	SQLLimit   int          `yaml:"sql_limit"`   // 默认SQL查询记录限制
	TotpEnable bool         `yaml:"totp_enable"` // 默认SQL查询记录限制
}
type TOTPConfig struct {
	Secrets map[string]string `json:"secrets"`
}

type ServerConfig struct {
	Port int `yaml:"port"`
}

type LogConfig struct {
	Enabled  bool   `yaml:"enabled"`
	FilePath string `yaml:"file_path"`
}

type Database struct {
	ID  string `yaml:"id"`
	DSN string `yaml:"dsn"`
}

// ExportRequest 定义前端请求结构
type ExportRequest struct {
	D string `json:"d"` // database_id
	S string `json:"s"` // sql
	T string `json:"t"` // Totp
	M int    `json:"m"` // max_records_per_file
}

var config Config
var logFile *os.File

func init() {
	// 读取配置文件
	configFile, err := os.ReadFile("config.yml")
	if err != nil {
		log.Fatalf("Error reading config file: %v", err)
	}

	// 解析配置文件
	if err := yaml.Unmarshal(configFile, &config); err != nil {
		log.Fatalf("Error parsing config file: %v", err)
	}
}

func setupLogger() {
	if !config.Log.Enabled {
		return
	}

	// 创建日志目录
	logDir := filepath.Dir(config.Log.FilePath)
	if err := os.MkdirAll(logDir, 0755); err != nil {
		log.Fatalf("Error creating log directory: %v", err)
	}

	// 打开日志文件
	var err error
	logFile, err = os.OpenFile(config.Log.FilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		log.Fatalf("Error opening log file: %v", err)
	}

	// 设置日志输出
	log.SetOutput(logFile)
}

// AES解密函数
func decryptAES(encrypted string, aesKey string) (string, error) {
	ciphertext, err := hex.DecodeString(encrypted)
	if err != nil {
		return "", err
	}

	key := []byte(aesKey)
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	// 使用ECB模式解密
	plaintext := make([]byte, len(ciphertext))
	for i := 0; i < len(ciphertext); i += aes.BlockSize {
		block.Decrypt(plaintext[i:i+aes.BlockSize], ciphertext[i:i+aes.BlockSize])
	}

	// 去除PKCS7填充
	padding := int(plaintext[len(plaintext)-1])
	return string(plaintext[:len(plaintext)-padding]), nil
}

func generateCodeForAccount(accountName string, accountCode string) (string, error) {
	if accountName == "" || accountCode == "" {
		log.Println("账号或编码不能为空")
		return "", errors.New("账号或编码不能为空")
	}

	// verificationCode := parts[1] // 验证码暂时未使用，但已提取

	// 获取密钥
	secret, err := server.GetSecret(accountName)
	if err != nil {
		log.Printf("获取密钥失败: %v\n", err)
		return "", errors.New("获取密钥失败")
	}

	// 生成当前的动态码
	code, err := totp.GenerateCode(secret, time.Now()) // 添加当前时间作为第二个参数

	if err != nil {
		log.Printf("生成动态码失败: %v\n", err)
		return "", errors.New("生成动态码失败")
	}
	// valid := totp.Validate(code, secret)
	// if !valid {
	// 	return "", errors.New("动态码验证失败")
	// }
	if code != accountCode {
		log.Printf("code %s accountCode %s\n", code, accountCode)
		return "", errors.New("验证码错误") // 验证码错误
	}
	return code, nil
}

func handleExport(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// 获取客户端IP
	clientIP := r.RemoteAddr
	if ip := r.Header.Get("X-Real-IP"); ip != "" {
		clientIP = ip
	} else if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
		clientIP = strings.Split(ip, ",")[0]
	}
	// 从r.Body读取十六进制文本
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Failed to read request body", http.StatusBadRequest)
		return
	}

	// 将十六进制字符串解码为字节
	hexStr := string(body)
	jsonBytes, err := hex.DecodeString(hexStr)
	if err != nil {
		http.Error(w, "Invalid   format", http.StatusBadRequest)
		return
	}

	// 解析JSON到ExportRequest结构体
	var req ExportRequest
	if err = json.Unmarshal(jsonBytes, &req); err != nil {
		http.Error(w, "Invalid JSON format", http.StatusBadRequest)
		return
	}

	// accountAndCode := ""
	if config.TotpEnable {
		// 解析TOTP信息
		// parts := strings.Split(req.T, ":")
		if req.T == "" || len(req.T) < 1 {
			http.Error(w, "验证码格式错误，例如xxxx:28729", http.StatusInternalServerError)
			return
		}

		// 拆分账号和验证码
		parts := strings.Split(req.T, ":")
		fmt.Println("parts", parts)
		if len(parts) != 2 {
			log.Println("账号格式不正确，应为 '用户名:验证码'")
			return
		}

		// 验证动态码
		_, err1 := generateCodeForAccount(parts[0], parts[1])

		// fmt.Println(code)
		if err1 != nil {
			http.Error(w, "验证码生成失败", http.StatusInternalServerError)
			return
		}
		// accountAndCode = parts[0] + ":" + code
		timestamp := time.Now().Unix()
		sha1Hash := sha1.Sum([]byte(req.T + "8872eeef" + fmt.Sprintf("%d", timestamp))) //操作人操作记录签名
		log.Printf("accountAndCode: %s   %x    %s", req.T, sha1Hash, fmt.Sprintf("%d", timestamp))
		// account, code := parts[0], parts[1]
		// 获取密钥
		// secret, err := server.GetSecret(account)

		// if err != nil {
		// 	fmt.Printf("获取密钥失败: %v\n", err)
		// 	return
		// }

		// valid := totp.Validate(code, secret)
		// if !valid {
		// 	http.Error(w, "动态码验证失败", http.StatusInternalServerError)
		// 	return
		// }
	}

	// now := time.Now()
	// formattedTime := now.Format("2006010215")
	// hash1 := sha256.New()
	// hash1.Write([]byte(accountAndCode + formattedTime))
	// hashValue := hash1.Sum(nil)
	// hashHex := hex.EncodeToString(hashValue)
	// //截取hashHex后面24位
	// hashHex = hashHex[len(hashHex)-24:]
	// // 解密数据库ID和SQL
	// // dbId := strings.ReplaceAll(req.D, "@", "=")
	// databaseID, err := decryptAES(req.D, hashHex)
	// if err != nil {
	// 	http.Error(w, "Failed d", http.StatusBadRequest)
	// 	return
	// }

	// // sqlStr := strings.ReplaceAll(req.S, "@", "=")
	// sqlQuery, err := decryptAES(req.S, hashHex)
	// if err != nil {
	// 	http.Error(w, "Failed s", http.StatusBadRequest)
	// 	return
	// }

	databaseID := req.D
	sqlQuery := req.S

	// databaseID := "ywzt-adb_tobacco_mp_kc"
	// sqlQuery := "SELECT * FROM  kc_enterprise"

	// 查找对应的数据库配置
	var dbConfig Database
	for _, db := range config.Databases {
		if db.ID == databaseID {
			dbConfig = db
			break
		}
	}

	if dbConfig.ID == "" {
		if config.Log.Enabled {
			log.Printf("........##### 错误：来自 %s 的请求使用的数据库ID %s 未找到", clientIP, databaseID)
		}
		http.Error(w, "id not found", http.StatusInternalServerError)
		return
	}

	// 检查SQL语句类型
	sqlType := strings.TrimSpace(strings.ToUpper(strings.Split(sqlQuery, " ")[0]))
	if sqlType != "SELECT" {
		if config.Log.Enabled {
			log.Printf("........##### 错误：来自 %s 的请求尝试执行非法的SQL类型：%s", clientIP, sqlType)
		}
		http.Error(w, "s error", http.StatusForbidden)
		return
	}

	// 检查SQL是否包含LIMIT子句，如果没有则添加默认限制
	defaultLimit := 300000 // 默认限制值
	if config.SQLLimit > 0 {
		defaultLimit = config.SQLLimit
	}

	sqlQueryUpper := strings.ToUpper(sqlQuery)
	if !strings.Contains(sqlQueryUpper, " LIMIT ") {
		sqlQuery = fmt.Sprintf("%s LIMIT %d", sqlQuery, defaultLimit)
		if config.Log.Enabled {
			log.Printf("为来自 %s 的查询自动添加LIMIT %d", clientIP, defaultLimit)
		}
	}
	// 连接数据库
	db, err := sql.Open("mysql", dbConfig.DSN)
	if err != nil {
		if config.Log.Enabled {
			log.Printf("........##### 错误：来自 %s 的请求连接数据库失败：%v", clientIP, err)
		}
		http.Error(w, "conn error", http.StatusInternalServerError)
		return
	}
	defer db.Close()

	// 执行查询
	rows, err := db.Query(sqlQuery)
	if err != nil {
		if config.Log.Enabled {
			log.Printf("........##### 错误：来自 %s 的请求执行SQL失败：%v", clientIP, err)
		}
		http.Error(w, "q error", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	// 获取列信息
	columns, err := rows.Columns()
	if err != nil {
		http.Error(w, "Error  columns", http.StatusInternalServerError)
		return
	}

	// 获取列类型信息
	columnTypes, err := rows.ColumnTypes()
	if err != nil {
		http.Error(w, "Error column types", http.StatusInternalServerError)
		return
	}

	// 生成唯一的文件名
	timestamp := time.Now().Format("20060102150405")
	zipFileName := fmt.Sprintf("export_%s.mp4", timestamp)

	// 创建缓存目录
	cacheDir := filepath.Join("cache", strings.TrimSuffix(zipFileName, ".mp4"))
	sqlDir := filepath.Join(cacheDir, "sql")
	if err := os.MkdirAll(sqlDir, 0755); err != nil {
		http.Error(w, "Error creating cache directory", http.StatusInternalServerError)
		return
	}
	defer os.RemoveAll(cacheDir)

	// 解析表名
	sqlParts := strings.Fields(strings.ToUpper(sqlQuery))
	tableName := ""
	for i, part := range sqlParts {
		if part == "FROM" && i+1 < len(sqlParts) {
			tableName = strings.TrimSpace(sqlParts[i+1])
			break
		}
	}
	if tableName == "" {
		http.Error(w, "无法解析t", http.StatusInternalServerError)
		return
	}

	// 准备数据处理
	columnStr := strings.Join(columns, ", ")
	var sqlFiles []string
	var currentFile *os.File
	var currentSQL strings.Builder
	var rowCount, fileCount, batchCount int

	// 写入数据
	for rows.Next() {
		// 每到达文件记录数限制或者是新的批次开始时，创建新文件
		if currentFile == nil || (req.M > 0 && rowCount >= req.M) {
			// 保存当前文件
			if currentFile != nil {
				currentSQL.WriteString(";")
				_, err = currentFile.WriteString(currentSQL.String())
				if err != nil {
					http.Error(w, "Error writing s file", http.StatusInternalServerError)
					return
				}
				currentFile.Close()
			}

			// 创建新文件
			fileCount++
			sqlFileName := fmt.Sprintf("export_%s_%d.sql", timestamp, fileCount)
			sqlFilePath := filepath.Join(sqlDir, sqlFileName)
			currentFile, err = os.Create(sqlFilePath)
			if err != nil {
				http.Error(w, "Error creating s file", http.StatusInternalServerError)
				return
			}
			sqlFiles = append(sqlFiles, sqlFilePath)

			// 重置计数器和SQL构建器
			rowCount = 0
			batchCount = 0
			currentSQL.Reset()
		}

		// 每40条记录开始新的INSERT语句
		if batchCount == 0 {
			if rowCount > 0 {
				currentSQL.WriteString(";\n")
			}
			currentSQL.WriteString(fmt.Sprintf("INSERT INTO %s (%s) VALUES ", tableName, columnStr))
		} else {
			currentSQL.WriteString(",\n")
		}

		// 扫描当前行数据
		values := make([]interface{}, len(columns))
		valuePtrs := make([]interface{}, len(columns))
		for i := range values {
			valuePtrs[i] = &values[i]
		}

		if err := rows.Scan(valuePtrs...); err != nil {
			http.Error(w, "Error scanning row", http.StatusInternalServerError)
			return
		}

		// 构建值部分
		currentSQL.WriteString("(")
		for i, value := range values {
			if i > 0 {
				currentSQL.WriteString(", ")
			}

			switch v := value.(type) {
			case nil:
				currentSQL.WriteString("NULL")
			case string:
				currentSQL.WriteString(fmt.Sprintf("'%s'", strings.ReplaceAll(v, "'", "''")))
			case []byte:
				// 检查字段类型
				columnType := columnTypes[i]
				databaseTypeName := strings.ToUpper(columnType.DatabaseTypeName())

				// 只有真正的二进制类型才使用UNHEX
				if databaseTypeName == "BINARY" || databaseTypeName == "VARBINARY" || databaseTypeName == "BLOB" || databaseTypeName == "TINYBLOB" || databaseTypeName == "MEDIUMBLOB" || databaseTypeName == "LONGBLOB" {
					hexStr := fmt.Sprintf("%x", v)
					currentSQL.WriteString(fmt.Sprintf("UNHEX('%s')", hexStr))
				} else {
					// 对于其他类型（如VARCHAR等），按字符串处理
					currentSQL.WriteString(fmt.Sprintf("'%s'", strings.ReplaceAll(string(v), "'", "''")))
				}
			case time.Time:
				currentSQL.WriteString(fmt.Sprintf("'%s'", v.Format("2006-01-02 15:04:05")))
			case bool:
				if v {
					currentSQL.WriteString("1")
				} else {
					currentSQL.WriteString("0")
				}
			default:
				currentSQL.WriteString(fmt.Sprintf("%v", v))
			}
		}
		currentSQL.WriteString(")")

		rowCount++
		batchCount++
		if batchCount >= 40 {
			batchCount = 0
		}
	}

	// 保存最后一个文件
	if currentFile != nil {
		currentSQL.WriteString(";")
		_, err = currentFile.WriteString(currentSQL.String())
		if err != nil {
			http.Error(w, "Error writing final s file", http.StatusInternalServerError)
			return
		}
		currentFile.Close()
	}

	// 创建ZIP文件
	zipFilePath := filepath.Join(cacheDir, zipFileName)
	zipFile, err := os.Create(zipFilePath)
	if err != nil {
		http.Error(w, "Error creating ZIP file", http.StatusInternalServerError)
		return
	}

	// 创建ZIP writer
	zipWriter := zip.NewWriter(zipFile)

	// 添加所有SQL文件到ZIP
	for _, sqlFilePath := range sqlFiles {
		sqlContent, err := os.ReadFile(sqlFilePath)
		if err != nil {
			http.Error(w, "Error reading s file", http.StatusInternalServerError)
			return
		}

		sqlFileWriter, err := zipWriter.Create(filepath.Base(sqlFilePath))
		if err != nil {
			http.Error(w, "Error adding s file to ZIP", http.StatusInternalServerError)
			return
		}

		_, err = sqlFileWriter.Write(sqlContent)
		if err != nil {
			http.Error(w, "Error writing to ZIP", http.StatusInternalServerError)
			return
		}
	}

	// 关闭ZIP writer
	zipWriter.Close()
	zipFile.Close()

	// 设置响应头 - 伪造成视频格式避免防火墙拦截
	w.Header().Set("Content-Type", "video/mp4")
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", zipFileName))

	// 发送ZIP文件
	zipContent, err := os.ReadFile(zipFilePath)
	if err != nil {
		http.Error(w, "Error reading ZIP file", http.StatusInternalServerError)
		return
	}

	_, err = w.Write(zipContent)
	if err != nil {
		if config.Log.Enabled {
			log.Printf("........##### 错误：发送ZIP文件到客户端 %s 失败：%v", clientIP, err)
		}
		return
	}

	// 记录成功的导出操作
	if config.Log.Enabled {
		log.Printf("客户端 %s 在 %s 成功导出查询，SQL：%s，总记录数：%d",
			clientIP,
			time.Now().Format("2006-01-02 15:04:05"),
			sqlQuery,
			rowCount)
	}
}

//go:embed  web
var content embed.FS

func main() {
	// 设置日志
	setupLogger()
	if logFile != nil {
		defer logFile.Close()
	}

	// 设置HTTP路由
	http.HandleFunc("/api/qdata", handleExport)
	// 设置静态文件服务
	http.Handle("/", server.SetupStaticFileServer(content))

	address := fmt.Sprintf(":%d", config.Server.Port)
	log.Printf("Server starting on %s...", address)
	log.Fatal(http.ListenAndServe(address, nil))
}
